package com.crois.barrier.web.handler;

import cn.hutool.core.collection.CollectionUtil;
import com.crois.barrier.common.dto.ApiRouteInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author Hou Ze Yu
 * @description 根据权重匹配可用apiRoute 处理器
 * @date 2020/7/20
 */
public class WeightRoundRobinApiRouteMatchHandler {

    static Logger LOGGER = LoggerFactory.getLogger(WeightRoundRobinApiRouteMatchHandler.class);

    private ConcurrentHashMap<String,ApiRouteWeightRoundRobin> apiRouteWeightRoundRobinMap =new ConcurrentHashMap<>();

    public ApiRouteInfo choose(List<ApiRouteInfo> apiRouteInfoList, String requestPath) {
            AtomicInteger notWeightNumber = new AtomicInteger(0);
            if (CollectionUtil.isNotEmpty(apiRouteInfoList)){
               for (ApiRouteInfo apiRouteInfo : apiRouteInfoList) {
                        if (apiRouteInfo.getWeight()==1){
                            notWeightNumber.getAndIncrement();
                        }
               }
               // 如果 是 都没有 weight 直接选择 最开始 的
               if (apiRouteInfoList.size()==notWeightNumber.get()){
                  return apiRouteInfoList.get(0);
               }
               // 如果 没有 则 进行 选择
                ApiRouteWeightRoundRobin apiRouteWeightRoundRobin = apiRouteWeightRoundRobinMap.get(requestPath);
               if (apiRouteWeightRoundRobin==null){
                   apiRouteWeightRoundRobin = new ApiRouteWeightRoundRobin();
                   apiRouteWeightRoundRobinMap.putIfAbsent(requestPath,apiRouteWeightRoundRobin);
               }
                return apiRouteWeightRoundRobin.choose(apiRouteInfoList);
            }
          return null;
    }

 static   class ApiRouteWeightRoundRobin {

        /**上次选择的服务器*/
        private  int currentIndex = -1;
        /**当前调度的权值*/
        private  int currentWeight = 0;
        /**最大权重*/
        private  int maxWeight;
        /**权重的最大公约数*/
        private  int gcdWeight;
        /**服务器数*/
        private  int serverCount;


        /*
         * 得到两值的最大公约数
         */
        public  int greaterCommonDivisor(int a, int b){
            if(a % b == 0){
                return b;
            }else{
                return greaterCommonDivisor(b,a % b);
            }
        }

        /*
         * 得到list中所有权重的最大公约数，实际上是两两取最大公约数d，然后得到的d
         * 与下一个权重取最大公约数，直至遍历完
         */
        public  int greatestCommonDivisor(List<ApiRouteInfo> servers){
            int divisor = 0;
            for(int index = 0, len = servers.size(); index < len - 1; index++){
                if(index ==0){
                    divisor = greaterCommonDivisor(
                            (int) servers.get(index).getWeight(),(int)  servers.get(index + 1).getWeight());
                }else{
                    divisor = greaterCommonDivisor(divisor, (int) servers.get(index).getWeight());
                }
            }
            return divisor;
        }

        /*
         * 得到list中的最大的权重
         */
        public  int greatestWeight(List<ApiRouteInfo> servers){
            int weight = 0;
            for(ApiRouteInfo server : servers){
                if(weight < server.getWeight()){
                    weight = (int) server.getWeight();
                }
            }
            return weight;
        }

     /***
      * 通过 ApiRouteInfo List 选择
      * @param apiRouteInfoList
      * @return
      */
        public  ApiRouteInfo choose(List<ApiRouteInfo> apiRouteInfoList) {
            init(apiRouteInfoList);
            LOGGER.info("[Begin WeightRoundRobinLoadBalanceRule Choose]");
            while(true){
                currentIndex = (currentIndex + 1) % serverCount;
                if(currentIndex == 0){
                    currentWeight = currentWeight - gcdWeight;
                    if(currentWeight <= 0){
                        currentWeight = maxWeight;
                        if(currentWeight == 0){
                            return null;
                        }
                    }
                }
                if( (apiRouteInfoList.get(currentIndex).getWeight()) >= currentWeight){
                    LOGGER.info("[WeightRoundRobinApiRouteMatch choose success]" +
                                    ",apiRouteName:{}" +
                                    ", apiRoutePath:{}" +
                                    ",apiRouteWeight:{}"
                            ,apiRouteInfoList.get(currentIndex).getApiRouteName()
                            ,apiRouteInfoList.get(currentIndex).getRequestPath()
                            ,apiRouteInfoList.get(currentIndex).getWeight());
                    return apiRouteInfoList.get(currentIndex);
                }
            }
        }

        private  void init(List<ApiRouteInfo> apiRouteInfoList) {
            maxWeight = greatestWeight(apiRouteInfoList);
            gcdWeight = greatestCommonDivisor(apiRouteInfoList);
            serverCount = apiRouteInfoList.size();
        }

    }
}
